package cluedo.gui;

import java.awt.BorderLayout;
import java.awt.FlowLayout;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.GridLayout;

import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;

import cluedo.ColorPawn;
import cluedo.Pawn;
import cluedo.Player;

import java.awt.CardLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.Insets;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;

import javax.swing.border.EtchedBorder;

public class PawnSelection {

	/**
	 * Frame padre di questo JDialog
	 */
	private JFrame ownerFrame;
	/**
	 * Questo JDialog
	 */
	private JDialog pawnDialog;
	
	// pannello informativo
	/**
	 * Etichetta che informa l'utente su cosa sta succedendo
	 */
	private JLabel infoLabel;
	/**
	 * Area di testo che informa l'utente delle scelte degli altri giocatori
	 */
	private JTextArea infoTextPane;
	
	// pannello bottoni
	/**
	 * Bottone ok/continua
	 */
	private JButton okButton;
	private ButtonsListener okButtonListener;
	
	// spareggio
	/**
	 * Bottone di lancio dado dello spareggio
	 */
	private JButton throwDiceButton;
	
	// scelta della pedina
	/**
	 * bottone carta-giocatore successiva
	 */
	private JButton nextButton;
	/**
	 * bottone carta-giocatore precedente
	 */
	private JButton previousButton;
	/**
	 * CardLayout per gestire lo scorrimento delle carte
	 */
	private CardLayout clPawns;
	/**
	 * Pannello con le carte da visualizzare
	 */
	private JPanel cardsPanel;
	/**
	 * Lista delle pedine rimaste, cioe' quelle fra cui il player puo' scegliere
	 */
	private List<Pawn> pawns;
	/**
	 * Indice per scorrere l'array colors
	 * Vale -1 in caso venga annullato
	 */
	private int pawnsIndex;
	/**
	 * Player che sta visualizzando questa finestra di dialogo
	 */
	private Player myPlayer;
	/**
	 * CardLayout per passare da modalita' "seleziona pedina" a modalita'
	 * "scegli con tiro di dado chi e' il primo giocatore"
	 */
	private CardLayout clModes;
	/**
	 * Pannello comprendente i due pannelli per le due modalita':
	 *  1. "seleziona pedina"
	 *  2. "scegli con tiro di dado chi e' il primo giocatore" 
	 */
	private JPanel modesPanel;
	private static final String PAWN_SEL_MODE= "Pawn Selection";
	private static final String DO_TIEBREAK_MODE= "Do Tie-Break";
	private static final String WAIT_TIEBREAK_MODE= "Wait Tie-Break";
	
	/**
	 * Visualizza una finestra di dialogo che avverte il giocatore che la partita
	 * e' stata annullata perche' tutti gli altri player sono usciti
	 */
	public void showAbortNewPlay() {
		JOptionPane.showMessageDialog(
				pawnDialog, 
				"Non so come dirtelo... ma tutti gli altri\n" +
				"giocatori sono andati via.\n\n" +
				"Torno alla finestra iniziale", 
				"Partita annullata", 
				JOptionPane.WARNING_MESSAGE);
	}
	
	/**
	 * Abilita il pulsante "Continua"
	 */
	public void showContinueDialog() {
		okButton.setText("Continua");
		okButton.setToolTipText("Chiudi la finestra e inizia a giocare");
		okButton.setActionCommand("continue");
		okButton.setEnabled(true);
		pawnDialog.getRootPane().setDefaultButton(okButton);
	}
	
	/**
	 * Visualizza la finestra di selezione della pedina
	 */
	public void showPawnSelectionDialog() {
		
		clModes.show(modesPanel, PAWN_SEL_MODE);
		pawnDialog.setTitle("Selezione giocatore - " + myPlayer.getNickname());
		infoTextPane.setText("");
		addInfo("Scelta dei personaggi...");
		
		pawnsIndex= 0; // inizializzazione
		if (myPlayer.isGroupOwner()) {
			// il proprietario e' il primo a scegliere la pedina
			enablePawn(new LinkedList<ColorPawn>(Arrays.asList(ColorPawn.allColors)));
		} else {
			// tutti gli altri devono aspettare
			disablePawn();			
		}

		// Apre la finestra di dialogo
		pawnDialog.pack();
		pawnDialog.setLocationRelativeTo(ownerFrame);
		pawnDialog.setVisible(true); // ritorna subito
	}
	
	/**
	 * Visualizza, eventualmente, la finestra che determina il primo giocatore
	 * tramite tiro di dato.
	 * Questo metodo va richiamato dopo showPawnSelectionDialog(), altrimenti
	 * il risultato non e' determinato.
	 */
	public void showFirstPlayerDialog() {
		
		// mantengo disabilitato okButton
		showContinueDialog();
		okButton.setEnabled(false);
		
		// bisogna fare lo spareggio
		pawnDialog.setTitle("Spareggio per il primo giocatore - " + myPlayer.getNickname());
		infoLabel.setText("Informazioni");
		addInfo("\nSpareggio per il primo turno");
		if (myPlayer.joinTieBreak()) {
			clModes.show(modesPanel, DO_TIEBREAK_MODE);
			pawnDialog.getRootPane().setDefaultButton(throwDiceButton);
			throwDiceButton.setEnabled(true);
			addInfo("Tu partecipi allo spareggio");
		} else {
			clModes.show(modesPanel, WAIT_TIEBREAK_MODE);
			pawnDialog.getRootPane().setDefaultButton(okButton);
			throwDiceButton.setEnabled(false);
			addInfo("Tu NON partecipi allo spareggio");
		}
		

	}
	
	/**
	 * Chiude la finestra di dialogo
	 */
	public void closePawnSelectionDialog() { 
		pawnDialog.setVisible(false);
		pawnDialog.dispose();
	}
	
	/**
	 * Disabilita la possibilita' di scegliere il colore della pedina.
	 * E' comunque sempre possibile annullare;
	 */
	public void disablePawn() {
		infoLabel.setText("Attesa altri giocatori");
		okButton.setEnabled(false);
		nextButton.setEnabled(false);
		previousButton.setEnabled(false);
	}
	
	/**
	 * Abilita la possibilita' di scegliere il colore della pedina.
	 * @param colors array di colori fra cui e' possibile scegliere
	 */
	public void enablePawn(List<ColorPawn> colors) {
		infoLabel.setText("Scegli il tuo colore");
		okButton.setEnabled(true);
		nextButton.setEnabled(true);
		previousButton.setEnabled(true);
		
		pawns= new LinkedList<Pawn>();
		cardsPanel.removeAll();
		for (ColorPawn cp : colors) {
			Pawn p= new Pawn(cp);
			pawns.add(p);
			cardsPanel.add(new JLabel(p.getImage()));
		}
	}

	/**
	 * Aggiunge un messaggio all'area dei messaggi informativi.
	 * @param str messaggio informatico
	 */
	public void addInfo(String str) {
		// TODO curare la parte stilistica del testo
		infoTextPane.append(str + "\n");
		infoTextPane.setCaretPosition(infoTextPane.getDocument().getLength());
	}
	
	/**
	 * Crea la finestra di dialogo per la selezione della propria pedina, ma
	 * senza visualizzarla.
	 * @param ownerFrame frame padre di questa finestra di dialogo
	 * @param myPlayer player che vuole selezionare la pedina
	 */
	public PawnSelection(JFrame ownerFrame, Player myPlayer) {
		
		this.ownerFrame= ownerFrame;
		this.myPlayer= myPlayer;

		okButtonListener= new ButtonsListener();
		
		initialize();
	}
	
	private void initialize() {

		// creo la finestra (non modale per mantenere la chat abilitata)
		pawnDialog= new JDialog(ownerFrame, false);
		pawnDialog.setResizable(false);
		JPanel mainPane= new JPanel();
		mainPane.setLayout(new GridLayout(1, 2));
		
		// a sinistra il pannello informatico
		mainPane.add(createInfoPane());
		
		// a destra il "pannello delle cose" (e' un semplice contenitore...)
		JPanel stuffsPane= new JPanel();
		stuffsPane.setLayout(new BorderLayout());
		mainPane.add(stuffsPane);
		
		// stuffsPane: in alto il card layout per "selezione pedina" / "primo giocatore"
		clModes= new CardLayout();
		modesPanel= new JPanel(clModes);
		addCards();
		stuffsPane.add(modesPanel, BorderLayout.NORTH);
		// imposto il content pane nella finestra principale
		pawnDialog.setContentPane(mainPane);

		// stuffsPane: in basso i bottoni ok/annulla
		JPanel buttonPane = new JPanel();
		buttonPane.setLayout(new FlowLayout(FlowLayout.CENTER));
		stuffsPane.add(buttonPane, BorderLayout.SOUTH);
		
		// bottone ok
		okButton = new JButton("OK");
		okButton.setToolTipText("Scegli questo personaggio");
		okButton.setActionCommand("okPawn");
		buttonPane.add(okButton);
		okButton.addActionListener(okButtonListener);
		okButton.addActionListener(myPlayer.new ContinueButtonListener());
		pawnDialog.getRootPane().setDefaultButton(okButton);
		// bottone cancel
		JButton cancelButton = new JButton("Annulla");
		cancelButton.setActionCommand("cancel");
		cancelButton.setToolTipText("Annulla ed esci dalla partita");
		cancelButton.addActionListener(okButtonListener);
		buttonPane.add(cancelButton);
		
		// gestione della chiusura
		pawnDialog.setDefaultCloseOperation(JDialog.DO_NOTHING_ON_CLOSE);
		pawnDialog.addWindowListener(new WindowAdapter() {
            public void windowClosing(WindowEvent we) {
				// esco dal mio gruppo
			
				PawnSelection.this.myPlayer.exitMyPlayGroup();
				
				closePawnSelectionDialog();
            }
		});
	}
	
	private JPanel createInfoPane() {
		JPanel infoPane= new JPanel();
		infoPane.setLayout(new GridBagLayout());
		
		// etichetta
		infoLabel= new JLabel("Informazioni");
		GridBagConstraints gbc_infoLabel = new GridBagConstraints();
		gbc_infoLabel.insets = new Insets(5, 5, 5, 5);
		gbc_infoLabel.gridx = 0;
		gbc_infoLabel.gridy = 0;
		gbc_infoLabel.fill = GridBagConstraints.HORIZONTAL;
		gbc_infoLabel.anchor = GridBagConstraints.NORTH;
		gbc_infoLabel.weightx = 1.0;
		infoPane.add(infoLabel, gbc_infoLabel);
		
		// area di testo
		infoTextPane= new JTextArea();
		infoTextPane.setEditable(false);
		infoTextPane.setBorder(new EtchedBorder(EtchedBorder.LOWERED, null, null));
		JScrollPane scrollPane= new JScrollPane();
		scrollPane.setViewportView(infoTextPane);
		GridBagConstraints gbc_infoTextPane = new GridBagConstraints();
		gbc_infoTextPane.insets = new Insets(5, 5, 5, 5);
		gbc_infoTextPane.gridx = 0;
		gbc_infoTextPane.gridy = 1;
		gbc_infoTextPane.fill = GridBagConstraints.BOTH;
		gbc_infoTextPane.anchor = GridBagConstraints.CENTER;
		gbc_infoTextPane.weightx = 1.0;
		gbc_infoTextPane.weighty = 1.0;
		infoPane.add(scrollPane, gbc_infoTextPane);
		infoTextPane.setWrapStyleWord(true);
		
		return infoPane;
	}
	
	/**
	 * Aggiunge le "card" a "modesPanel"
	 */
	private void addCards() {
		
		// *********************************************************************
		// selezione della pedina
		
		JPanel selPane= new JPanel();
		
		selPane.setLayout(new BorderLayout());
		cardsPanel= new JPanel();
		selPane.add(cardsPanel, BorderLayout.CENTER);
		
		// bottone successivo
		nextButton = new JButton(">");
		nextButton.setToolTipText("Prossimo");
		nextButton.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				clPawns.next(cardsPanel);
				pawnsIndex++;
				if (pawnsIndex>pawns.size()-1) pawnsIndex= 0;
			}
		});
		selPane.add(nextButton, BorderLayout.LINE_END);
		// bottone precedente
		previousButton = new JButton("<");
		previousButton.setToolTipText("Precedente");
		previousButton.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				clPawns.previous(cardsPanel);
				pawnsIndex--;
				if (pawnsIndex<0) pawnsIndex= pawns.size()-1;
			}
		});
		selPane.add(previousButton, BorderLayout.LINE_START);
				
		// card layout
		clPawns= new CardLayout();
		cardsPanel.setLayout(clPawns);
		
		pawnsIndex= -1;
		cardsPanel.add(new JLabel(new ImageIcon(getClass().getResource("/img/nobody.jpg"))));
		
		// ed infine aggiungo 
		modesPanel.add(selPane, PAWN_SEL_MODE);
		
		// *********************************************************************
		// spareggio
		
		
		// caso 1: c'e' uno spareggio ma questo giocatore non vi partecipa
		modesPanel.add(
				new JLabel("<html>Attendi che gli altri<br>eseguano lo spareggio</html>"),
				WAIT_TIEBREAK_MODE
				);
		
		// caso 2: io partecipo allo spareggio
		JPanel fstPane= new JPanel();
		throwDiceButton= new JButton("Spareggia");
		throwDiceButton.setToolTipText("Tira il dado");
		throwDiceButton.setActionCommand("doTieBreak");
		throwDiceButton.addActionListener(okButtonListener);
		fstPane.add(throwDiceButton);
		modesPanel.add(fstPane, DO_TIEBREAK_MODE);
	}
	
	private class ButtonsListener implements ActionListener {
		@Override
		public void actionPerformed(ActionEvent e) {
			String str= e.getActionCommand();
			
			
			if (str.equals("okPawn")) {
				// selezione della pedina
				disablePawn();
				myPlayer.setPawn(pawns.get(pawnsIndex));
				
			} else if (str.equals("doTieBreak")) {
				// fai lo spareggio
				
				// disabilito il bottone per evitare che vengano fatti piu' spareggi
				throwDiceButton.setEnabled(false);
				
				myPlayer.doTieBreak();
			} else if (str.equals("cancel")) {
				// esco dal mio gruppo
				myPlayer.exitMyPlayGroup();
				closePawnSelectionDialog();
			}
		}
	}
}
